﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Xml;
using System.IO;
using System.Text;
using System.Linq;

public enum PlistType
{
    _none,
    _string,
    _integer,
    _array,
    _bool,
    _dict,
};

public class PlistNode
{
    public PlistType type = PlistType._none;
    public string stringValue = "";
    public int intValue = 0;
    public bool boolValue = false;
    public PlistNode[] arrayValue = null;
    public Dictionary<string, PlistNode> dictValue = null;

    public bool SetString(string _value)
    {
        if (type != PlistType._string) return false;
        stringValue = _value;
        return true;
    }

    public bool SetBool(bool _value)
    {
        if (type != PlistType._bool) return false;
        boolValue = _value;
        return true;
    }

    public bool SetInteger(int _value)
    {
        if (type != PlistType._integer) return false;
        intValue = _value;
        return true;
    }
}

public class PlistDoc
{
    Dictionary<string, PlistNode> dict = null;

    public PlistDoc(Dictionary<string, PlistNode> _data)
    {
        dict = _data;
    }

    public bool removeNode(string _key)
    {
        bool ret = false;
        if(dict!=null)
        {
            ret = dict.Remove(_key);
        }
        return ret;
    }

    public PlistNode getNode(string _key)
    {
        return dict[_key];
    }

    public bool SetString(string _key, string _value)
    {
        if (dict == null) return false;
        if (dict.ContainsKey(_key))
        {
            PlistNode t = dict[_key];
            t.stringValue = _value;
        }
        else
        {
            PlistNode t = new PlistNode();
            t.type = PlistType._string;
            t.stringValue = _value;
            dict.Add(_key, t);
        }
        return true;
    }

    public bool SetBool(string _key, bool _value)
    {
        if (dict == null) return false;
        if (dict.ContainsKey(_key))
        {
            PlistNode t = dict[_key];
            t.boolValue = _value;
        }
        else
        {
            PlistNode t = new PlistNode();
            t.type = PlistType._bool;
            t.boolValue = _value;
            dict.Add(_key, t);
        }
        return true;
    }

    public bool SetInteger(string _key, int _value)
    {
        if (dict == null) return false;
        if (dict.ContainsKey(_key))
        {
            PlistNode t = dict[_key];
            t.intValue = _value;
        }
        else
        {
            PlistNode t = new PlistNode();
            t.type = PlistType._integer;
            t.intValue = _value;
            dict.Add(_key, t);
        }
        return true;
    }
    
    public bool savePlistWithPath(string _filePath)
    {
        bool ret = true;
        string data = PlistParse.FormatDocToString(dict);
        System.IO.StreamWriter file = new System.IO.StreamWriter(_filePath, false);
        file.Write(data);
        file.Close();
        file.Dispose();

        return ret;
    }
}

public class PlistParse
{

    private static PlistParse instance;
    private static string _tableString = "  ";
    private PlistParse()
    {

    }
    public static PlistParse GetInstance()
    {
        if (instance == null)
        {
            instance = new PlistParse();
        }
        return instance;
    }

    void setValueByXmlNode(PlistNode _in, XmlNode _node)
    {
        switch (_in.type)
        {
            case PlistType._string:
                _in.stringValue = _node.InnerText;
                break;
            case PlistType._integer:
                _in.intValue = int.Parse(_node.InnerText);
                break;
            case PlistType._bool:
                if (_node.Name.Equals("true"))
                {
                    _in.boolValue = true;
                }
                else
                {
                    _in.boolValue = false;
                }
                break;
        }
    }

    public static PlistType getTypeByString(string _content)
    {
        switch(_content)
        {
            case "string":
                return PlistType._string;                
            case "integer":
                return PlistType._integer;
            case "array":
                return PlistType._array;
            case "dict":
                return PlistType._dict;                
            case "true":
               return PlistType._bool;
            case "false":
                return PlistType._bool;
        }
        return PlistType._none;
    }

    public static string getStringByType(PlistType _type)
    {
        switch (_type)
        {
            case PlistType._string:
                return "string";
            case PlistType._integer:
                return "integer";
            case PlistType._array:
                return "array";
            case PlistType._dict:
                return "dict";
        }
        return "";
    }

    void parseDictNode(XmlNodeList inNode, out Dictionary<string, PlistNode> _outDict)
    {
        _outDict = null;
        var allNodeList = inNode;
        if (allNodeList != null)
        {
            _outDict = new Dictionary<string, PlistNode>();
            string tmpKey = "";
           
                foreach (XmlNode courseNode in allNodeList)
                {

                    if (courseNode.Name.Equals("key"))
                    {
                        tmpKey = courseNode.InnerText;
                        _outDict.Add(tmpKey, new PlistNode());
                    }
                    else
                    {
                        PlistNode tmp = null;
                        if (_outDict.TryGetValue(tmpKey, out tmp))
                        {
                            tmp.type = getTypeByString(courseNode.Name);
                            switch (tmp.type)
                            {
                                case PlistType._array:
                                    {
                                        var arrayNodeList = courseNode.ChildNodes;
                                        if (arrayNodeList != null)
                                        {
                                            tmp.arrayValue = new PlistNode[arrayNodeList.Count];
                                            int i = 0;
                                            foreach (XmlNode arrayNode in arrayNodeList)
                                            {
                                                tmp.arrayValue[i] = new PlistNode();
                                                tmp.arrayValue[i].type = getTypeByString(arrayNode.Name);
                                                setValueByXmlNode(tmp.arrayValue[i], arrayNode);
                                                i++;
                                            }
                                        }
                                    }
                                    break;
                                case PlistType._dict:
                                    {
                                        parseDictNode(courseNode.ChildNodes, out tmp.dictValue);
                                    }
                                    break;
                                case PlistType._string:
                                case PlistType._integer:
                                case PlistType._bool:
                                    setValueByXmlNode(tmp, courseNode);
                                    break;
                            }
                        }
                    }
                  
                }
 
        }
    }

    public Dictionary<string, PlistNode> parseFromFileToDict(string _filePath)
    {
        string xmlStr = File.ReadAllText(_filePath, Encoding.UTF8);
        XmlDocument doc = new XmlDocument();
        doc.LoadXml(xmlStr);
        XmlNodeList rowNoteList = doc.SelectNodes("/plist/dict");

        Dictionary<string, PlistNode> retDict = new Dictionary<string,PlistNode>();

        foreach (XmlNode rowNode in rowNoteList)
        {
            var fieldNodeList = rowNode.ChildNodes;
            parseDictNode(fieldNodeList, out retDict);
        }

        return retDict;
    }

    public static PlistDoc parseFromFile(string _filePath)
    {
        Dictionary<string, PlistNode> tmp = PlistParse.GetInstance().parseFromFileToDict(_filePath);
        PlistDoc doc = new PlistDoc(tmp);
        return doc;
    }

    public static string FromatNode(string key, PlistNode _node, int _level)
    {
        if(_node.type==PlistType._none)
        {
            return "";
        }

        string _pre = "";
        for (int i = 0; i < _level; i++ )
        {
            _pre += _tableString;
        }
        string ret = "";
        if(key!=null && key.Length!=0)
        {
            ret += _pre+"<key>" + key + "</key>\n";
        }

        if (_node.type != PlistType._bool)
        {
            ret += _pre + "<" + getStringByType(_node.type) + ">";
            switch(_node.type)
            {
                case PlistType._integer:
                    ret += _node.intValue;
                    break;
                case PlistType._string:
                    ret += _node.stringValue;
                    break;
                case PlistType._array:
                    {
                        ret += "\n";
                        for (int i = 0; i < _node.arrayValue.Length; i++ )
                        {
                            ret += FromatNode("", _node.arrayValue[i], 3);
                            ret += "\n";
                        }
                        ret += _node.stringValue;
                        ret += _pre;
                    }
                    break;
                case PlistType._dict:
                    {
                        ret += "\n";
                        Dictionary<string, PlistNode> dic1Asc = _node.dictValue.OrderBy(o => o.Key).ToDictionary(o => o.Key, p => p.Value);
                        foreach (KeyValuePair<string, PlistNode> kv in dic1Asc)
                        {
                            ret += FromatNode(kv.Key, kv.Value, 3);
                            ret += "\n";
                        }
                        ret += _pre;
                    }
                    break;
            }
            ret += "</" + getStringByType(_node.type) + ">";
        }
        else
        {
            ret += _pre + "<" + (_node.boolValue ? "true" : "false") + " />";
        }
        return ret;
    }

    public static string FormatDocToString(Dictionary<string, PlistNode> _data)
    {
        string ret = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<plist version=\"1.0\">\n  <dict>\n";
        Dictionary<string, PlistNode> dic1Asc = _data.OrderBy(o => o.Key).ToDictionary(o => o.Key, p => p.Value);
        foreach (KeyValuePair<string, PlistNode> kv in dic1Asc)
        {
            ret += FromatNode(kv.Key, kv.Value, 2);
            ret += "\n";
        }
        ret += "  </dict>\n</plist>";
        return ret;
    }
}